// Copyright 2014 G&W. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package wscom

import (
    "time"
	"strconv"
	"errors"
	"net/http"
	"encoding/json"
	"dhfshop/models/im"
	"dhfshop/utils/response"
	"dhfshop/service/redisgo"
	"github.com/astaxie/beego"
	"github.com/gorilla/websocket"
)

const (
	writeWait      = 30 * time.Second //Time allowed to write a message to the peer.
	pongWait       = 10 * time.Second //Time allowed to read the next pong message from the peer.
	maxMessageSize = 1024             //Maximum message size allowed from peer.
)

type connection struct {
	Identity string       `json:"identity"` //客户端identity
	CliSck   string       `json:"clisck"`   //客户端ip:port
	SvrIp    string       `json:"svr_ip"`   //服务端ip
	SvrPort  string       `json:"svr_port"` //服务端port
	Token    string       `json:"token"`    //标示该连接的随机字符。
	Device   string       `json:"device"`   //设备类型（web、ios、android、winphone）
	disabled int          `json:"disabled"` //0打开 1断开
	User     *im.UserDao `json:"user"`     //用户句柄
}
type ConnectSvr struct {
	ws *websocket.Conn //websocket connection
	connection
}
/*
func NewConnection() *connection {
	return &connection{
		User: new(im.UserDao),
	}
}
*/
func NewConnectSvr() *ConnectSvr {
	return &ConnectSvr{
		connection: connection{User: new(im.UserDao)},
	}
}

/**
 * 连接
 */
func (c *ConnectSvr) Connect(w http.ResponseWriter, r *http.Request, client string, userId int) (int, error) {
	uidStr := strconv.Itoa(userId)
	ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
	if _, ok := err.(websocket.HandshakeError); ok {
		return 4007, errors.New(response.LangConf.Get("4007").MustString())
	} else if err != nil {
		return 4008, errors.New(response.LangConf.Get("4008").MustString())
	}
	c.ws = ws
	c.Device = client
	c.connection.User.Uid = uidStr
	c.Token = client + "_" + uidStr
	//添加连接池
	//ConnectHub.register <- c
	ConnectHub.add(c)
	return 0, err
}

/**
 * 循环读取消息
 */
func (c *ConnectSvr) Reader(userId int64) (err error) {
	redisconn := redisgo.GetInstance()
	cacheKey := "customer_" + strconv.FormatInt(userId, 10)
	onlineInfo := struct {
		Status int `json:"status"`
	}{}
	onlineInfo.Status = 1
	if onlineJSON, err := json.Marshal(onlineInfo); err == nil { 
		if _, err = redisconn.Set(cacheKey, string(onlineJSON), 3*24*60*60); err != nil {  
			beego.Debug("SETEX", err.Error())
		}
	}
	defer func() {
		token, _ := c.getToken()
		ConnectHub.close(token)
		//
		onlineInfo.Status = 0
		if onlineJSON, err := json.Marshal(onlineInfo); err == nil { 
			if _, err = redisconn.Set(cacheKey, string(onlineJSON), 3*24*60*60); err != nil {  
				beego.Debug("SETEX", err.Error())
			}
		}
	}()
	c.ws.SetReadLimit(maxMessageSize)
	c.ws.SetReadDeadline(time.Now().Add(pongWait))
	c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, message, err := c.ws.ReadMessage()
		if err != nil {
			break
		}
		requestSrv := NewRequestSrv()
		requestSrv.ParserMsg(c, message)
	}
	return err
}

func (c *ConnectSvr) discon() error {
	c.disabled = 1
	c.ws.Close()
	return nil
}

func (c *ConnectSvr) getToken() (string, error) {
	if c.disabled == 1 {
		return c.Token, errors.New(response.LangConf.Get("3000").MustString())
	}
	return c.Token, nil
}

func (c *ConnectSvr) getDevice() (string, error) {
	if c.disabled == 1 {
		return "", errors.New(response.LangConf.Get("3000").MustString())
	}
	return c.Device, nil
}

func (c *ConnectSvr) setUser(user *im.UserDao) (bool, error) {
	if c.disabled == 1 {
		return false, errors.New(response.LangConf.Get("3000").MustString())
	}
	c.User = user
	return true, nil
}

func (c *ConnectSvr) getUser() (*im.UserDao, error) {
	if c.disabled == 1 {
		return nil, errors.New(response.LangConf.Get("3000").MustString())
	}
	if user := c.User; user == nil || user.Uid == "" {
		return nil, errors.New(response.LangConf.Get("4011").MustString())
	}
	return c.User, nil
}

func (c *ConnectSvr) sendText(message []byte) error {
	if _, err := c.getToken(); err != nil {
		return errors.New(response.LangConf.Get("3000").MustString())
	}
	return c.write(websocket.TextMessage, message)
}

func (c *ConnectSvr) sendBinary(message []byte) error {
	if _, err := c.getToken(); err != nil {
		return errors.New(response.LangConf.Get("3000").MustString())
	}
	return c.write(websocket.BinaryMessage, message)
}

func (c *ConnectSvr) ping() error {
	return c.write(websocket.PingMessage, []byte{})
}

/**
 * write writes a message with the given message type and payload.
 */
func (c *ConnectSvr) write(mt int, payload []byte) error {
	c.ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.ws.WriteMessage(mt, payload)
}
